package com.walkline.dianping;

import java.io.UnsupportedEncodingException;
import java.util.Enumeration;
import java.util.Hashtable;

import javax.microedition.io.HttpConnection;

import net.rim.blackberry.api.phone.Phone;
import net.rim.device.api.io.transport.ConnectionFactory;
import net.rim.device.api.util.Comparator;
import net.rim.device.api.util.SimpleSortingVector;

import org.json.me.JSONException;
import org.json.me.JSONObject;
import org.json.me.JSONTokener;

import com.walkline.app.DianpingAppConfig;
import com.walkline.dianping.dao.DianpingCategories;
import com.walkline.dianping.dao.DianpingCities;
import com.walkline.dianping.dao.DianpingDealIdList;
import com.walkline.dianping.dao.DianpingDealResult;
import com.walkline.dianping.dao.DianpingRegions;
import com.walkline.dianping.inf.Categories;
import com.walkline.dianping.inf.Cities;
import com.walkline.dianping.inf.DealIdList;
import com.walkline.dianping.inf.DealResult;
import com.walkline.dianping.inf.Regions;
import com.walkline.util.Function;
import com.walkline.util.network.HttpClient;

public class DianpingSDK
{
	protected ConnectionFactory _cf;
	protected HttpClient _http;

	protected String _appKey = DianpingConfig.client_ID;
	protected String _appSecret = DianpingConfig.client_SERCRET;

	public static DianpingSDK getInstance()
	{
		return new DianpingSDK();
	}

	protected DianpingSDK()
	{
		_cf = new ConnectionFactory();
		_cf.setPreferredTransportTypes(DianpingAppConfig.preferredTransportTypes);
		_cf.setDisallowedTransportTypes(DianpingAppConfig.disallowedTransportTypes);
		_cf.setTimeoutSupported(true);
		_cf.setAttemptsLimit(10);
		_cf.setRetryFactor(2000);
		_cf.setConnectionTimeout(120000);

		_http = new HttpClient(_cf);
	}

	public String getPhoneNumberProvince()
	{
		String phoneNumber = Phone.getDevicePhoneNumber(false);
		String province = "";
		Hashtable params = new Hashtable();

		params.put("tel", phoneNumber);

		try {
			String sb = _http.doGet("http://tcc.taobao.com/cc/json/mobile_tel_segment.htm", params).toString();

			sb = sb.substring(sb.indexOf("{"), sb.lastIndexOf('}')+1);
			JSONObject jsonObject = new JSONObject(sb);
			if ((jsonObject != null) || (jsonObject.length() > 0))
			{
				try {
					province = new String(jsonObject.optString("province").getBytes(), "gbk");
				} catch (UnsupportedEncodingException e) {}
			}
		} catch (DianpingException e) {Function.errorDialog(e.toString());} catch (JSONException e) {
		} catch (Exception e) {}

		return province;
	}

	public byte[] doRequestRAW(String path) throws DianpingException
	{
		byte[] result = null;
		StringBuffer responseBuffer = null;

		try {
			responseBuffer = checkResponse(_http.doGet(path));				

			if ((responseBuffer == null) || (responseBuffer.length() <= 0)) {result = null;}

			result = responseBuffer.toString().getBytes();
		} catch (Exception e) {throw new DianpingException(e.getMessage());}

		return result;
	}

	public Cities getCitiesWithBusinesses(JSONObject jo) throws DianpingException {return new DianpingCities(this, jo);}
	public Cities getCitiesWithBusinesses() throws DianpingException {return getCitiesWithBusinesses(null, null);}
	public Cities getCitiesWithBusinesses(final AsyncCallback listener, final Object state) throws DianpingException
	{
		if (listener != null) {
			new Thread() {
				public void run() {
					try {
						Cities result = null;
						result = getCitiesWithBusinesses();
						listener.onComplete(result);
					} catch (Exception e) {
						listener.onException(e);
					}
				}
			}.start();

			return null;
		} else {
			Cities result = null;
			Hashtable params = new Hashtable();

			String sign = generateSignature(params);
			params.put("appkey", _appKey);
			params.put("sign", sign);

			JSONObject jsonObject = doRequest(HttpConnection.GET, DianpingConfig.getCitiesWithBusinesses, params);
			if ((jsonObject == null) || (jsonObject.length() <= 0)) {
				result = null;
			} else {
				result = getCitiesWithBusinesses(jsonObject);
			}

			return result;
		}
	}

	public Regions getRegionsWithBusinesses(JSONObject jo) throws DianpingException {return new DianpingRegions(this, jo);}
	public Regions getRegionsWithBusinesses() throws DianpingException {return getRegionsWithBusinesses(null, null, null);}
	public Regions getRegionsWithBusinesses(String city) throws DianpingException {return getRegionsWithBusinesses(city, null, null);}
	public Regions getRegionsWithBusinesses(final String city, final AsyncCallback listener, final Object state) throws DianpingException
	{
		if (listener != null) {
			new Thread() {
				public void run() {
					try {
						Regions result = null;
						result = getRegionsWithBusinesses(city);
						listener.onComplete(result);
					} catch (Exception e) {
						listener.onException(e);
					}
				}
			}.start();

			return null;
		} else {
			Regions result = null;
			Hashtable params = new Hashtable();

			if (city != null && city.length() > 0) {params.put("city", city);}

			String sign = generateSignature(params);

			params.put("appkey", _appKey);
			params.put("sign", sign);

			JSONObject jsonObject = doRequest(HttpConnection.GET, DianpingConfig.getRegionsWithBusinesses, params);
			if ((jsonObject == null) || (jsonObject.length() <= 0)) {
				result = null;
			} else {
				result = getRegionsWithBusinesses(jsonObject);
			}

			return result;
		}
	}

	public Categories getCategoriesWithBusinesses(JSONObject jo) throws DianpingException {return new DianpingCategories(this, jo);}
	public Categories getCategoriesWithBusinesses() throws DianpingException {return getCategoriesWithBusinesses(null, null, null);}
	public Categories getCategoriesWithBusinesses(String city) throws DianpingException {return getCategoriesWithBusinesses(city, null, null);}
	public Categories getCategoriesWithBusinesses(final String city, final AsyncCallback listener, final Object state) throws DianpingException
	{
		if (listener != null) {
			new Thread() {
				public void run() {
					try {
						Categories result = null;
						result = getCategoriesWithBusinesses(city);
						listener.onComplete(result);
					} catch (Exception e) {
						listener.onException(e);
					}
				}
			}.start();

			return null;
		} else {
			Categories result = null;
			Hashtable params = new Hashtable();

			if (city != null && city.length() > 0) {params.put("city", city);}

			String sign = generateSignature(params);

			params.put("appkey", _appKey);
			params.put("sign", sign);

			JSONObject jsonObject = doRequest(HttpConnection.GET, DianpingConfig.getCategoriesWithBusinesses, params);
			if ((jsonObject == null) || (jsonObject.length() <= 0)) {
				result = null;
			} else {
				result = getCategoriesWithBusinesses(jsonObject);
			}

			return result;
		}
	}

	public Cities getCitiesWithDeals(JSONObject jo) throws DianpingException {return new DianpingCities(this, jo);}
	public Cities getCitiesWithDeals() throws DianpingException {return getCitiesWithDeals(null, null);}
	public Cities getCitiesWithDeals(final AsyncCallback listener, final Object state) throws DianpingException
	{
		if (listener != null) {
			new Thread() {
				public void run() {
					try {
						Cities result = null;
						result = getCitiesWithDeals();
						listener.onComplete(result);
					} catch (Exception e) {
						listener.onException(e);
					}
				}
			}.start();

			return null;
		} else {
			Cities result = null;
			Hashtable params = new Hashtable();

			String sign = generateSignature(params);
			params.put("appkey", _appKey);
			params.put("sign", sign);

			JSONObject jsonObject = doRequest(HttpConnection.GET, DianpingConfig.getCitiesWithDeals, params);
			if ((jsonObject == null) || (jsonObject.length() <= 0)) {
				result = null;
			} else {
				result = getCitiesWithDeals(jsonObject);
			}

			return result;
		}
	}

	public DealIdList getDailyNewIdList(JSONObject jo) throws DianpingException {return new DianpingDealIdList(this, jo);}
	public DealIdList getDailyNewIdList(final String city, final String date) throws DianpingException {return getDailyNewIdList(city, date, null, null, null);}
	public DealIdList getDailyNewIdList(final String city, final String date, final String category) throws DianpingException {return getDailyNewIdList(city, date, category, null, null);}
	public DealIdList getDailyNewIdList(final String city, final String date, final AsyncCallback listener, final Object state) throws DianpingException {return getDailyNewIdList(city, date, null, listener, state);}
	public DealIdList getDailyNewIdList(final String city, final String date, final String category, final AsyncCallback listener, final Object state) throws DianpingException
	{
		if (listener != null) {
			new Thread() {
				public void run() {
					try {
						DealIdList result = null;
						if (category == null)
						{
							result = getDailyNewIdList(city, date);	
						} else {
							result = getDailyNewIdList(city, date, category);
						}

						listener.onComplete(result);
					} catch (Exception e) {
						listener.onException(e);
					}
				}
			}.start();

			return null;
		} else {
			DealIdList result = null;
			Hashtable params = new Hashtable();

			params.put("city", city);
			params.put("date", date);
			if (category != null && category.length() > 0) {params.put("category", category);}

			String sign = generateSignature(params);
			params.put("appkey", _appKey);
			params.put("sign", sign);

			JSONObject jsonObject = doRequest(HttpConnection.GET, DianpingConfig.getDailyNewIdList, params);
			if ((jsonObject == null) || (jsonObject.length() <= 0)) {
				result = null;
			} else {
				result = getDailyNewIdList(jsonObject);
			}

			return result;
		}
	}

	public DealResult getDealsDetail(JSONObject jo) throws DianpingException {return new DianpingDealResult(this, jo);}
	public DealResult getDealsDetail(String[] dealIds) throws DianpingException {return getDealsDetail(dealIds, null, null);}
	public DealResult getDealsDetail(final String[] dealIds, final AsyncCallback listener, final Object state) throws DianpingException
	{
		if (listener != null) {
			new Thread() {
				public void run() {
					try {
						DealResult result = null;
						result = getDealsDetail(dealIds);
						listener.onComplete(result);
					} catch (Exception e) {
						listener.onException(e);
					}
				}
			}.start();

			return null;
		} else {
			DealResult result = null;
			Hashtable params = new Hashtable();
			String api = DianpingConfig.getSingleDeal;

			if (dealIds.length == 1)
			{
				params.put("deal_id", dealIds[0]);
			} else if (dealIds.length > 1) {
				StringBuffer sb = new StringBuffer();

				for (int i=0; i<dealIds.length; i++)
				{
					sb.append(dealIds[i]);

					if (i < dealIds.length-1) {sb.append(",");}
				}

				params.put("deal_ids", sb.toString());
				api = DianpingConfig.getBatchDealsById;
			}
			
			String sign = generateSignature(params);
			params.put("appkey", _appKey);
			params.put("sign", sign);

			JSONObject jsonObject = doRequest(HttpConnection.GET, api, params);
			if ((jsonObject == null) || (jsonObject.length() <= 0)) {
				result = null;
			} else {
				result = getDealsDetail(jsonObject);
			}

			return result;
		}
	}

//**************************************************************************************************
//
//             //              //      //    //      ////////////////            //      //        
//             //            //    //  //  //                    //          //  //  //  //        
// //////////  //          //////////  ////        //          //      //        //      //        
//     //    ////////////              //      //  //  //    //    //  //  ////////////  ////////  
//     //      //      //    ////////    ////////  //    //  //  //    //      ////    //    //    
//     //      //      //    //    //              //        //        //    //  ////    //  //    
//     //      //      //    ////////  //          //    //  //  //    //  //    //  //  //  //    
//     //      //      //    //    //  //    //    //  //    //    //  //      //        //  //    
//     ////////        //    ////////  //  //      //        //        //  //////////    //  //    
// ////      //        //    //    //  ////    //  //      ////        //    //    //      //      
//         //          //    //    //  //      //  //                  //      ////      //  //    
//       //        ////      //  ////    ////////  //////////////////////  ////    //  //      //  
//
//**************************************************************************************************

	private JSONObject doRequest(String methond, String api, Hashtable params) throws DianpingException
	{
		JSONObject result = null;
		StringBuffer responseBuffer = null;

		try {
			if (methond.equals(HttpConnection.POST))
			{
				responseBuffer = checkResponse(_http.doPost(api, params));	
			} else {
				responseBuffer = checkResponse(_http.doGet(api, params));
			}

			if ((responseBuffer == null) || (responseBuffer.length() <= 0)) {result = null;}

			result = new JSONObject(new JSONTokener(new String(responseBuffer.toString().getBytes(), "utf-8")));
		} catch (Exception e) {
			throw new DianpingException(e.getMessage());
		}

		return result;
	}

	public StringBuffer checkResponse(StringBuffer res) throws DianpingException
	{
		StringBuffer result = null;

		try {
			if ((res != null) && (res.length() > 0)) {
				if ((res.charAt(0) == '{') && (res.charAt(res.length() - 1) == '}'))
				{
					JSONObject jo = new JSONObject(new JSONTokener(res.toString()));
					if ((jo != null) && jo.has("error"))
					{
						JSONObject jsonObject = jo.getJSONObject("error");

						int errorCode = jsonObject.optInt("errorCode");
						String errorMessage = new String(jsonObject.optString("errorMessage").getBytes(), "utf-8");
						throw new DianpingException("\n\nerrorCode: " + errorCode + "\nerrorMessage: " + errorMessage);
					}
				}
			}
		} catch (JSONException e) {Function.errorDialog(e.toString());}
		  catch (UnsupportedEncodingException e) {Function.errorDialog(e.toString());}

		result = res;

		return result;
	}

	private String generateSignature(Hashtable params)
	{
		if (params != null && params.size() > 1) {params = sortParams(params);}

		StringBuffer paramBuffer = new StringBuffer();

		paramBuffer.append(_appKey);
		Enumeration keys = params.keys();
		while (keys.hasMoreElements())
		{
			String key = (String) keys.nextElement();
			
			paramBuffer.append(key).append(params.get(key));
		}
		paramBuffer.append(_appSecret);

		String result = "";
		try {
			result = Digest.sha1Hash(paramBuffer.toString().getBytes("utf-8"));
		} catch (UnsupportedEncodingException e) {}
		
		return result;
	}

	/**
	 * 对所有请求参数进行字典升序排列
	 * @param params 请求参数表
	 * @return 排序后的新表
	 */
	private Hashtable sortParams(Hashtable params)
	{
		Hashtable newParams = new Hashtable();
		SimpleSortingVector tempParams = new SimpleSortingVector();

		Enumeration keys = params.keys();
		while (keys.hasMoreElements())
		{
			String key = (String) keys.nextElement();
			tempParams.addElement(key);
		}

		tempParams.setSortComparator(new ComparatorObject());
		tempParams.reSort();

		keys = tempParams.elements();
		while (keys.hasMoreElements())
		{
			String key = (String) keys.nextElement();
			String value = (String) params.get(key);
			
			newParams.put(key, value);
		}

		return newParams;
	}
}

class ComparatorObject implements Comparator
{
	public int compare (Object key1,Object key2)
	{
		return ((((String) key1).compareTo((String) key2)));
	}
}